home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************************************
- * *
- * FPB.c - Copyright 1993 - 1995 Chris Larson (larson@cs.ucla.edu), All rights reserved *
- * *
- * Source file of a CDEF which mimics the progress bar used in the Finder. This source file and its *
- * compiled derivatives may be freely used within any freeware/shareware/postcardware/beerware/… as *
- * long as you mention my name in your credits. Neither this source nor its compiled derivatives are *
- * in the public domain and may not be use in any form in public domain software. Neither this source *
- * nor its compiled derivatives may be used in any form in a commercial product without the expressed,*
- * written consent of the author (me). *
- * *
- * Version 2.0 -- March 10, 1995. *
- * *
- *****************************************************************************************************/
-
- #ifndef powerc
- #include <A4Stuff.h>
- #endif
-
- #include <Palettes.h>
- #include "FinderProgressBar.h"
- #include "FPB.h"
-
- //-----
- // Some global variables.
- //-----
-
- static const RGBColor gBlackColor = { 0x0000, 0x0000, 0x0000 };
- static const RGBColor gWhiteColor = { 0xFFFF, 0xFFFF, 0xFFFF };
- static const RGBColor gBlueColor = { 0xCCCC, 0xCCCC, 0xFFFF };
- static const RGBColor gGrayColor = { 0x4444, 0x4444, 0x4444 };
- static const Pattern gMyGrayPattern = { { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 } };
-
- //----------------------------------------------------------------------------------------------------
- //
- // main -- Handle everything but draw messages.
- //
- //----------------------------------------------------------------------------------------------------
-
- pascal long main(short varCode, ControlHandle theControlHandle, short message, long param)
- {
- SignedByte controlRecState; // Holds the state of the control record’s handle.
- long returnValue; // Holds the value returned by the CDEF.
- ControlPtr theControl; // Holds a pointer to the control record.
-
- #ifndef powerc
-
- // ----------
- // Since I use some static global variables I need to set up A4 so they can be accessed
- // properly; only needed on 680x0 systems.
- // ----------
-
- long oldA4 = SetCurrentA4();
-
- #endif
-
- // ----------
- // Lock down the control record.
- // ----------
-
- controlRecState = HGetState((Handle)theControlHandle);
- HLock((Handle)theControlHandle);
-
- // ----------
- // Initialize the return value and the control pointer (since the record is locked it is safe
- // to use a pointer). The return value is initialized to 0 since most CDEF messages want 0
- // returned.
- // ----------
-
- returnValue = 0;
- theControl = *theControlHandle;
-
- switch (message) {
-
- // ----------
- // Ignore draw messages if the control is not visible.
- // ----------
-
- case drawCntl:
- if ( theControl->contrlVis )
- BeginDraw(theControlHandle, varCode);
- break;
-
- // ----------
- // Test the control: return kInProgressBarControlPart if the given point is within the
- // control's rectangle, 0 otherwise (the whole bar is one part).
- // ----------
-
- case testCntl:
- if ( PtInRect(*(Point*)(¶m),&(theControl->contrlRect)) )
- returnValue = kInProgressBarControlPart;
- break;
-
- // ----------
- // Initialize the control: determine if Color Quickdraw exists and should be used. Actually,
- // this simply looks to see if the control’s owning port is a color port. If so, it infers
- // the existence of CQD and uses it. If not, we’re drawing into a B/W port anyway, so who
- // cares if CQD is present?
- // ----------
-
- case initCntl:
- theControl->contrlData = (Handle)(((theControl->contrlOwner->portBits.rowBytes) & kIsColorPort) == kIsColorPort);
- break;
-
- // ----------
- // For the 24-bit mode region calculation message, the high bit of the given region handle
- // must be cleared. Note that I don’t test this bit to determine which region to calculate.
- // Since this control has no indicator (part with a part code >= 129) I will never receive
- // a message asking for indicator region calculation.
- // ----------
-
- case calcCRgns:
- param &= 0x7FFFFFFF;
-
- // ----------
- // Calculate the control’s region.
- // ----------
-
- case calcCntlRgn:
- RectRgn((RgnHandle)param,&(theControl->contrlRect));
- break;
-
- // ----------
- // All other messages do nothing and return 0.
- // ----------
-
- default:
- break;
- }
-
- // ----------
- // Restore the state of the control record’s handle.
- // ----------
-
- HSetState ((Handle)theControlHandle, controlRecState);
-
- // ----------
- // Restore A4 (only on 680x0).
- // ----------
-
- #ifndef powerc
-
- SetA4(oldA4);
-
- #endif
-
- // ----------
- // We’re outa here.
- // ----------
-
- return returnValue;
- }
-
- //----------------------------------------------------------------------------------------------------
- //
- // BeginDraw -- Save off all the stuff we’re going to change, figure out which colors to use (and
- // whether we need to dither) and call the appropriate drawing routine.
- //
- //----------------------------------------------------------------------------------------------------
-
- void BeginDraw(ControlHandle theControlHandle, short varCode)
- {
- GrafPtr savePort;
- Handle aHandle, bHandle;
- RgnHandle oldClip;
- ControlPtr theControl = *theControlHandle;
- long useColor = (long)(theControl->contrlData);
- Boolean doDither = false;
- Rect theBox = theControl->contrlRect;
- PenState oldPen;
- RGBColor oldFore, oldBack;
- RGBColor frameColor = gBlackColor, barColor = gGrayColor, bodyColor = gBlueColor;
- RGBColor frameDim, barDim, bodyDim, windowBack;
-
- // ----------
- // Set the port to the control’s port. Very probably not needed (since Apple’s CDEFs do not
- // do this) but just to be extra safe…
- // ----------
-
- GetPort(&savePort);
- SetPort(theControl->contrlOwner);
-
- // ----------
- // Save and reset the pen state
- // ----------
-
- GetPenState(&oldPen);
- PenNormal();
-
- // ----------
- // If we are going to draw in color, set up everything for the color environment. Otherwise, the
- // only thing to check is to see if the bar is inactive (and therefore dithered).
- // ----------
-
- if ( useColor ) {
-
- //----------
- // Save the old colors.
- //----------
-
- GetForeColor(&oldFore);
- GetBackColor(&oldBack);
-
- //----------
- // If we’re supposed to use custom colors, retrieve the colors from the color table,
- // overwriting the default colors.
- //----------
-
- if ( varCode == kFPBCustomColorVarCode ) {
-
- GetAuxiliaryControlRecord(theControlHandle,(AuxCtlHandle*)(&bHandle));
- aHandle = (Handle)((**((AuxCtlHandle)bHandle)).acCTable);
-
- frameColor = *FindColorInTable((CCTabHandle)aHandle,cFPBFrameColorID);
- barColor = *FindColorInTable((CCTabHandle)aHandle,cFPBBarColorID);
- bodyColor = *FindColorInTable((CCTabHandle)aHandle,cFPBBodyColorID);
- }
-
- //----------
- // Retrieve the window content color (background color) of the control’s owner.
- //----------
-
- GetAuxWin(theControl->contrlOwner,(AuxWinHandle*)(&bHandle));
- aHandle = (Handle)((**((AuxWinHandle)bHandle)).awCTable);
-
- windowBack = *FindColorInTable((CCTabHandle)aHandle,wContentColor);
-
- //----------
- // If the bar is to be dimmed, check to see if the appropriate dimmed colors exist. If they
- // all exist, we’ll use them to draw. If not all exist, then draw in the regular colors
- // and dither the result to “dim” it. This is for consistency -- either we want the whole
- // thing in dimmed colors or we want the whole thing dithered; not parts of both.
- //----------
-
- if ( theControl->contrlHilite == kControlInactiveControlPart ) {
-
- frameDim = frameColor;
- barDim = barColor;
- bodyDim = bodyColor;
-
- aHandle = (Handle)GetGDevice();
-
- if ( GetGray((GDHandle)aHandle,&windowBack,&frameDim)
- && GetGray((GDHandle)aHandle,&windowBack,&barDim)
- && GetGray((GDHandle)aHandle,&windowBack,&bodyDim) ) {
-
- frameColor = frameDim;
- barColor = barDim;
- bodyColor = bodyDim;
-
- } else {
-
- doDither = true;
- }
- }
-
- } else if ( theControl->contrlHilite == kControlInactiveControlPart ) {
-
- doDither = true;
- }
-
- //----------
- // Draw the bar’s frame. Since this is always the same and will always lie within the control’s
- // rectangle (thus making the clipping region mind the control’s rectangle would have no effect)
- // we can do it here, before mucking with the clipping region.
- //----------
-
- if ( useColor ) {
-
- RGBForeColor(&frameColor);
- RGBBackColor(&windowBack);
- }
-
- if ( doDither )
- PenPat(&gMyGrayPattern);
-
- FrameRect(&theBox);
- InsetRect(&theBox,1,1);
-
- if ( doDither )
- PenNormal();
-
- //----------
- // Save the current port’s clip region and set it to the intersection of the clip region with
- // the newly inset rectangle (so it won’t allow overwriting of the freshly drawn frame).
- //----------
-
- oldClip = NewRgn();
- GetClip(oldClip);
-
- aHandle = (Handle)NewRgn();
- RectRgn((RgnHandle)aHandle,&theBox);
- SectRgn((RgnHandle)aHandle,oldClip,(RgnHandle)aHandle);
- SetClip((RgnHandle)aHandle);
- DisposeRgn((RgnHandle)aHandle);
-
- //----------
- // Figure out if we are supposed to draw the “Barber Pole” or the regular bar. Use the barber pole
- // when the high word of the control’s RfCon is 0x7FFF. In that case, take the frame number from
- // the low word of the RfCon. Frames are numbered from 0 to ( kFPBFrameCount - 1 ), inclusive.
- //----------
-
- if ( HiWord(theControl->contrlRfCon) == 0x7FFF )
-
- DrawBarberPoleBar(theControl,&barColor,&bodyColor);
-
- else
-
- DrawNormalBar(theControl,&barColor,&bodyColor);
-
- //----------
- // Dither the progress bar if we’re supposed to
- //----------
-
- if ( doDither ) {
-
- if ( useColor )
- RGBBackColor(&windowBack);
-
- PenMode(notPatBic);
- PenPat(&gMyGrayPattern);
- PaintRect(&theControl->contrlRect);
- }
-
- // ----------
- // Restore all the stuff we munged: colors (if needed), pen state, clip region, and current port.
- // ----------
-
- if ( useColor ) {
-
- RGBForeColor(&oldFore);
- RGBBackColor(&oldBack);
- }
-
- SetPenState(&oldPen);
-
- SetClip(oldClip);
- DisposeRgn(oldClip);
-
- SetPort(savePort);
- }
-
- //----------------------------------------------------------------------------------------------------
- //
- // DrawNormalBar -- Draw the progress bar according to the control settings using the colors given
- // in the parameters.
- //
- //----------------------------------------------------------------------------------------------------
-
- void DrawNormalBar(ControlPtr theControl, RGBColorPtr barColor, RGBColorPtr bodyColor)
- {
- long useColor = (long)(theControl->contrlData);
- Rect theBox = theControl->contrlRect;
-
- //----------
- // Set up theBox to draw the progress bar.
- //----------
-
- InsetRect(&theBox,1,1);
- theBox.right = CalculateBarBoundry(theBox.left,theBox.right,theControl);
-
- //----------
- // Set up colors so that the bar will map to black on a 1-bit device; then draw the bar.
- //----------
-
- if ( useColor ) {
-
- RGBForeColor(barColor);
- RGBBackColor(&gWhiteColor);
- }
-
- PaintRect(&theBox);
-
- //----------
- // Now set up theBox to draw the “empty” space not yet filled by the bar.
- //----------
-
- theBox.left = theBox.right;
- theBox.right = theControl->contrlRect.right - 1;
-
- //----------
- // Set up colors so that the space will map to white on a 1-bit device; then draw the space.
- //----------
-
- if ( useColor ) {
-
- RGBForeColor(bodyColor);
- RGBBackColor(&gBlackColor);
-
- } else
- PenMode(patBic);
-
- PaintRect(&theBox);
- }
-
- //----------------------------------------------------------------------------------------------------
- //
- // DrawBarberPoleBar -- Draw the correct frame of the “Barber Pole” animation using the given colors.
- //
- //----------------------------------------------------------------------------------------------------
-
- void DrawBarberPoleBar(ControlPtr theControl, RGBColorPtr barColor, RGBColorPtr bodyColor)
- {
- long useColor = (long)(theControl->contrlData);
- Rect theBox = theControl->contrlRect;
- short counter, height, frameNumber;
-
- InsetRect(&theBox,1,1);
-
- //----------
- // Set the pen to be the width of one stripe.
- //----------
-
- PenSize(kBarberPoleStripeWidth,1);
-
- //----------
- // Set up the colors. On a 1-bit device, one of these should map to white and the other black. It
- // doesn’t _really_ matter which maps to which as long as they’re different.
- //----------
-
- if ( useColor ) {
-
- RGBForeColor(barColor);
- RGBBackColor(bodyColor);
- }
-
- //----------
- // Compute the horizontal starting point for drawing. Note that each drawing loop draws one
- // stripe of each color (with gray drawn first). This starting point is set far enough to the
- // left to make the first set of stripes drawn hit the lower left corner of the bar.
- //----------
-
- height = theBox.bottom - theBox.top;
- frameNumber = LoWord(theControl->contrlRfCon);
-
- counter = height + frameNumber + 2 * kBarberPoleStripeWidth - 2;
- counter = counter / ( 2 * kBarberPoleStripeWidth );
- counter = theBox.left - counter * 2 * kBarberPoleStripeWidth + frameNumber;
-
- //----------
- // Now back bias the starting location to account for the first increment.
- //----------
-
- counter = counter - kBarberPoleStripeWidth;
-
- //----------
- // Until we would start drawing to the right of the bar, draw a pair of lines, slanting to the
- // right, first of the two in the bar color, second in the fill color.
- //----------
-
- while ( counter < theBox.right ) {
-
- counter += kBarberPoleStripeWidth;
- PenMode(patCopy);
- MoveTo(counter,theBox.top);
- LineTo(counter + height, theBox.bottom - 1);
-
- counter += kBarberPoleStripeWidth;
- PenMode(patBic);
- MoveTo(counter,theBox.top);
- LineTo(counter + height, theBox.bottom - 1);
- }
- }
-
- //----------------------------------------------------------------------------------------------------
- //
- // CalculateBarBoundry -- Given the edges of the progress bar and the control’s setting, determine
- // where the right edge of the progress bar belongs. This calculation
- // corresponds to the following equation:
- //
- // ( boxRight - boxLeft ) * ( controlValue - controlMinimum )
- // result = boxLeft + ----------------------------------------------------------
- // ( controlMaximum - controlMinimum )
- //
- // on the PowerPC architecture, this is implemented in C; while on the 680x0
- // it is implemented in assembly. This is done because the 68000 does not
- // support 32-bit multiplies in hardware, 32-bit multiplies are not strictly
- // necessary (the 16->32 multiply and 32->16 divide will suffice), and the
- // compiler was not using the mulu.w and divu.w instructions correctly. (Well,
- // it was using them correctly in the C sense, but that’s not what I wanted
- // them to do.)
- //----------------------------------------------------------------------------------------------------
-
- #ifdef powerc
-
- short CalculateBarBoundry(short boxLeft, short boxRight, ControlPtr theControl)
- {
- short result = boxLeft;
- short min = theControl->contrlMin;
- short max = theControl->contrlMax;
- short val = theControl->contrlValue;
- long top = ( boxRight - boxLeft ) * ( val - min );
- long bottom = max - min;
-
- if ( bottom != 0 )
- result += top / bottom;
-
- return result;
- }
-
- #else
-
- //----------
- // A couple of notes about the assembly language. This method will calculate the correct answer no
- // matter what values are given as long as the following conditions hold:
- // (1) boxRight >= boxLeft, and
- // (2) contrlMax >= contrlValue >= contrlMin.
- // Both of these should be met in normal circumstances. If these restrictions hold, then the
- // differences (both in the numerator and the one in the denominator) will fit into 16-bit unsigned
- // integers. That means that I can use the mulu.w and divu.w instructions to perform the multiply
- // and divide, instead of requiring the addition of the software library for 32 bit multiplication
- // on the 68000 (The software library was twice the size of this code resource last time I checked).
- // The only other thing to note is that a division by zero can’t happen. The denominator is zero
- // only when contrlMax == contrlMin. If this is the case, then contrlValue == contrlMin and the
- // branch before the multiply will be taken, so the division instruction is never reached.
- //----------
-
-
- asm short CalculateBarBoundry(short boxLeft, short boxRight, ControlPtr theControl)
- {
- fralloc // Give ourselves a stack frame
-
- move.w boxRight,d1 // Right edge of the box to d1
- move.w boxLeft,d0 // Left edge of the box to d0
- sub.w d0,d1 // Width of the box to d0
-
- movea.l theControl,a0 // Store the control pointer in a0
- move.w struct (ControlRecord.contrlValue)(a0),d2 // Control’s value to d2
- sub.w struct (ControlRecord.contrlMin)(a0),d2 // Normalized value to d2
-
- beq @1 // If normalized value == 0, exit. (Note
- // that d0 already holds left edge)
-
- mulu.w d2,d1 // box width * normalized value to d1
-
- move.w struct (ControlRecord.contrlMax)(a0),d2 // Control’s max to d2
- sub.w struct (ControlRecord.contrlMin)(a0),d2 // Normalized maximum to d2
-
- divu.w d2,d1 // width * value / maximum to d1. Note that
- // divide by zero can’t happen here.
-
- add.w d1,d0 // Offset left edge by amount in d1
-
- @1:
- frfree // Remove the stack frame
- rts // Outa here...
- }
-
- #endif
-
- //----------------------------------------------------------------------------------------------------
- //
- // FindColorInTable -- Return a pointer to the RGBColor matching the given id. Return the first entry
- // in the table if none match. Note that this routine will not cause memory to
- // move, but since it returns a pointer which references a handle’s block, one
- // must be sure to lock the handle if the pointer is going to be used in or
- // after a call that may move memory (like RGBForeColor() or RGBBackColor()).
- //
- //----------------------------------------------------------------------------------------------------
-
- RGBColor* FindColorInTable(CCTabHandle colorTable, short id)
- {
- short counter;
-
- for ( counter = (**colorTable).ctSize; counter != 0; counter-- ) {
-
- if ( (**colorTable).ctTable[counter].value == id )
- break;
- }
-
- return ( &( (**colorTable).ctTable[counter].rgb ));
- }
-